/*
 * IBM Corporation.
 * Copyright (c) 2014 All Rights Reserved.
 */

package com.ibm.iisp.common.file.util;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * 类作用：
 * 
 * @author Johnny@cn.ibm.com 使用说明：
 */
@Component
public class FileStoreUtils {
	@Value("${file.storage.root}")
	String fileStorageRoot;

	@Autowired(required = false)
	CustomDocSysHelper customDocSysHelper;
	public final static String TEMP_PATH = "/tmp";

	public String uploadFileToDocSys(String uri, String distPath, boolean isUpdate) throws IOException {
		if (customDocSysHelper == null) {
			return copyFile(uri, distPath, true);
		}
		return customDocSysHelper.uploadFile(uri, null, isUpdate);
	}

	public String downloadFile(String uri) {
		if (customDocSysHelper == null) {
			return getAbsFilePath(uri);
		}
		return customDocSysHelper.downloadFile(uri);
	}
	
	/**
	 * @param srcFilePath
	 *            源文件名，相对 FileStoreUtils.fileStorageRoot
	 * @param distPath
	 *            目标文件路径
	 * @param encodeFileName
	 *            是否对文件名编码（用于避免重名和保密）
	 * @return 新的文件名（绝对路径）
	 * @throws IOException
	 *             IOException
	 */
	public String copyFile(String srcFilePath, String distPath, boolean encodeFileName) throws IOException {
		File srcFile = new File(fileStorageRoot + srcFilePath);
		try (InputStream is = new FileInputStream(srcFile)) {
			// distPath
			String absPath = fileStorageRoot + distPath;
			File distFile = new File(absPath);
			if (distFile.exists() && distFile.isDirectory()) {
				distPath = distPath + "/" + srcFile.getName();
			}
			return uploadBinFile(distPath, is, true, true);
		}
	}
	
	/**
	 * 缺省对文件名编码，并且override If Exists
	 * 
	 * @param filePath
	 *            文件名
	 * @param in
	 *            文件内容的InputStream
	 * @return 真正保存了的文件名
	 * @throws IOException
	 *             IOException
	 */
	public String uploadBinFile(String filePath, InputStream in) throws IOException {
		return uploadBinFile(filePath, in, true, true);
	}

	/**
	 * 缺省对文件名编码，并且override If Exists
	 * 
	 * @param filePath
	 *            文件名
	 * @param content
	 *            文件内容
	 * @return 真正保存了的文件名
	 * @throws IOException
	 *             IOException
	 */
	public String uploadTxtFile(String filePath, String content) throws IOException {
		return uploadTxtFile(filePath, content, true, true);
	}
	
	/**
	 * @param filePath
	 *            文件名
	 * @param in
	 *            文件内容
	 * @param encodeFileName
	 *            是否对文件名编码
	 * @param overrideIfExists
	 *            如果文件已经存在，是否override?
	 * @return 真正保存了的文件名和路径
	 * @throws IOException
	 *             IOException
	 */
	public String uploadBinFile(String filePath, InputStream in, boolean encodeFileName, boolean overrideIfExists) throws IOException {
		if (encodeFileName) {
			filePath = encodeFileName(filePath);
		}
		File file = realFile(filePath, overrideIfExists);
		byte[] buf = new byte[10240];
		BufferedInputStream bis = new BufferedInputStream(in);
		try (FileOutputStream fos = new FileOutputStream(file)) {
			int len = bis.read(buf);
			while (len > 0) {
				fos.write(buf, 0, len);
				len = bis.read(buf);
			}
		}
		return filePath;
	}

	/**
	 * @param filePath
	 *            文件名
	 * @param content
	 *            文件内容
	 * @param encodeFileName
	 *            是否对文件名编码
	 * @param overrideIfExists
	 *            如果文件已经存在，是否override?
	 * @return 真正保存了的文件名
	 * @throws IOException
	 *             IOException
	 */
	public String uploadTxtFile(String filePath, String content, boolean encodeFileName, boolean overrideIfExists) throws IOException {
		if (encodeFileName) {
			filePath = encodeFileName(filePath);
		}
		File file = realFile(filePath, overrideIfExists);
		try (FileWriter fos = new FileWriter(file)) {
			fos.write(content);
		}
		return filePath;
	}

	/**
	 * 把上传好的文件移动到指定的目录下
	 * 
	 * @param sourceFile
	 *            要移动的源文件（相对）路径
	 * @param targetFolder
	 *            要移动到哪个目录（相对）
	 * @param encodeFileName
	 *            是否要加密文件名
	 * @param overrideIfExists
	 *            文件存在时是否要覆盖
	 * @return 移动后的文件路径，相对 fileUploadRoot
	 */
	public String moveFileToDir(String sourceFile, String targetFolder, boolean encodeFileName, boolean overrideIfExists) {
		if (sourceFile.indexOf(fileStorageRoot) < 0) {
			sourceFile = fileStorageRoot + sourceFile;
		}
		File srcFile = new File(sourceFile);
		String fn = srcFile.getName();
		checkFolder(targetFolder);
		String newFn = targetFolder;
		if (!newFn.endsWith("/")) {
			newFn += "/";
		}
		newFn += fn;
		if (encodeFileName) {
			newFn = encodeFileName(newFn);
		}
		srcFile.renameTo(new File(this.fileStorageRoot + newFn));
		return newFn;
	}

	/**
	 * @param targetFolder
	 */
	private void checkFolder(String targetFolder) {
		File dstPath = new File(this.getAbsFilePath(targetFolder));
		if (!dstPath.exists()) {
			dstPath.mkdirs();
			File folder1 = dstPath;
			while (targetFolder.contains(folder1.getName())) {
				folder1.setExecutable(true, false);
				folder1 = folder1.getParentFile();
			}
		}
	}

	/**
	 * @param sourceFile
	 *            要移动的源文件路径
	 * @param targetFile
	 *            要移动到的文件相对路径
	 * @param encodeFileName
	 *            是否对文件名编码
	 * @param overrideIfExists
	 *            是否覆盖已经存在的文件
	 * @return 移动后的相对文件路径
	 */
	public String moveFile(String sourceFile, String targetFile, boolean encodeFileName, boolean overrideIfExists) {
		if (sourceFile.indexOf(fileStorageRoot) < 0) {
			sourceFile = fileStorageRoot + sourceFile;
		}
		File srcFile = new File(sourceFile);
		String fn = targetFile.substring(targetFile.lastIndexOf("/") + 1);
		String targetFolder = targetFile.substring(0, targetFile.lastIndexOf("/"));
		checkFolder(targetFolder);
		File dstPath = new File(this.fileStorageRoot + targetFolder);
		String newFn = dstPath.getPath();
		if (!newFn.endsWith(File.separator)) {
			newFn += File.separator;
		}
		newFn += fn;
		if (encodeFileName) {
			newFn = encodeFileName(newFn);
		}
		srcFile.renameTo(new File(newFn));
		return getRelativeFilePath(newFn);
	}
	public String encodeFileName(String filePath){
		int pathIdx = filePath.lastIndexOf("/") + 1;
		String fp = "";
		String fn = filePath;
		if (pathIdx > 0) {
			fp = filePath.substring(0, pathIdx);
			fn = filePath.substring(pathIdx);
		}
		pathIdx = fn.lastIndexOf(".");
		String extFn = "";
		if (pathIdx > 0) {
			extFn = fn.substring(pathIdx);
			fn = fn.substring(0, pathIdx);
		}
		fn = fn + RandomStringUtils.randomAlphanumeric(6);
		return fp + fn + extFn;
	}

	File realFile(String filePath, boolean overrideIfExists) {
		checkFolder(filePath);
		String absFilePath = getAbsFilePath(filePath);
		/*
		 * File file = new File(absFilePath.substring(0, absFilePath.lastIndexOf("/"))); if (!file.exists()) { file.mkdirs();
		 * file.setExecutable(true, false); }
		 */
		File file = new File(absFilePath);
		int fileIndex = 0;
		while (file.exists()) {
			if (overrideIfExists) {
				file.delete();
			} else {
				int idx = absFilePath.lastIndexOf(".");
				file = new File(absFilePath.substring(0, idx) + fileIndex + absFilePath.substring(idx));
			}
		}
		return file;
	}

	/**
	 * @param relativePath
	 *            文件的相对路径，如："/abc.txt", "/p1/foo.xml"
	 * @return 保存在文件系统中的绝对路径
	 */
	public String getAbsFilePath(String relativePath){
		if (!relativePath.startsWith("/")) {
			relativePath = "/" + relativePath;
		}
		return fileStorageRoot + relativePath;
	}

	/**
	 * @param relativePath
	 *            文件的相对路径，如："/abc.txt", "/p1/foo.xml"
	 * @return 保存在文件系统中的临时文件夹下的绝对路径
	 */
	public String getTempAbsFilePath(String relativePath) {
		if (!relativePath.startsWith("/")) {
			relativePath = "/" + relativePath;
		}
		return fileStorageRoot + TEMP_PATH + relativePath;
	}

	public String getRelativeFilePath(String absPath) {
		return absPath.substring(fileStorageRoot.length());
	}

	/**
	 * 删除目录（文件夹）以及目录下的文件
	 * 
	 * @param sPath
	 *            被删除目录的相对路径
	 */
	public void deleteDirectory(String sPath) {
		// 如果sPath不以文件分隔符结尾，自动添加文件分隔符
		if (!sPath.endsWith(File.separator)) {
			sPath += File.separator;
		}

		File dirFile = new File(fileStorageRoot + sPath);
		// 如果dir对应的文件不存在，或者不是一个目录，则退出
		if (!dirFile.exists() || !dirFile.isDirectory()) {
			return;
		}
		// 删除文件夹下的所有文件(包括子目录)
		File[] files = dirFile.listFiles();
		for (int i = 0; i < files.length; i++) {
			// 删除子文件
			if (files[i].isFile()) {
				removeFile(files[i].getAbsolutePath());
			} else {
				deleteDirectory(files[i].getAbsolutePath());
			}
		}
		dirFile.delete();
	}

	// /**
	// * 删除单个文件
	// *
	// * @param sPath
	// * 被删除文件的文件名
	// * @return 单个文件删除成功返回true，否则返回false
	// */
	// public void deleteFile(String sPath) {
	// if (!sPath.startsWith(fileStorageRoot)) {
	// sPath = fileStorageRoot + sPath;
	// }
	// File file = new File(sPath);
	// // 路径为文件且不为空则进行删除
	// if (file.isFile() && file.exists()) {
	// file.delete();
	// }
	// }

	/**
	 * 删除指定的文件
	 * 
	 * @param sPath
	 *            要删除的文件路径,相对或绝对路径
	 * @return 文件删除了为true, 文件没找到为false
	 */
	public boolean removeFile(String sPath) {
		if (!sPath.startsWith(fileStorageRoot)) {
			sPath = fileStorageRoot + sPath;
		}
		File file = new File(getAbsFilePath(sPath));
		if (file.exists()) {
			file.delete();
			return true;
		}
		return false;
	}
}
